<?php
/** 
 * @author Erling Owe <erling.owe@gmail.com>
 * @version 0.9
 * @package Core
 * @subpackage Data
 */ 

/**
 * @todo Make this class work with multiple XML files.
 */
class Core_Data_Generator {

    private $arrDefinitionFilenames = array();

    public function addDefinition ( $strDefinitionFilename ) {
        $this->arrDefinitionFilenames[] = $strXMLFilename;
    }

    /**
     * Generates the database setup.
     *
     * Any new tables or fields are appended to the database. Any tables or
     * fields in the database not found in the provided XMLs are suffixed
     * '-deprecated'.
     *
     * Currently only index 0 is used. Then database itself, when using MySQL,
     * will not be created, and thus must be cretated manually.
     *
     * @param Core_Data_SQLConnectionEx $SQLConnectionEx
     * @param array $arrXMLFilenames
     * @param boolean $boolWipe Wipe the entire database before updating.
     */
    public function generateDatabases ( Core_Data_SQL_Connection $Connection, $boolWipe = false ) {

        // Throw an exception if no database is set in $SQLConnection.
        if ( !$Connection->issetDatabase() ) {
            throw new Exception( "No database is set in the Core_Data_SQLConnection object." );
        }


        $DefinitionInterpreter = new Core_Data_Generator_DefinitionInterpreter( $this->arrDefinitionFilenames );





        // Get the existing tables.
        $strQuery = "SHOW TABLES";
        $arrResult = $SQLConnection->fetch( $strQuery );
        $arrExistingTables = array();
        foreach ( $arrResult as $table ) {
            $assoc = "Tables_in_" . $SQLConnection->getCurrentDatabase();
            $arrExistingTables[] = $table[$assoc];
        }

        print_r( $arrExistingTables ); // to be removed



        // TODO: Compare arrays and suffix deprecated tables.
        //       DO NOT suffix tables that are already suffixed.

        foreach ( $XMLInterpreter->Tables as $Table ) {

            if ( !in_array( $Table->Name, $arrExistingTables ) ) {

                // TODO: Create new table with its fields.

            }

            $arrExistingFields = $this->SQLConnection->getFields( $Table->Name );

            // TODO: Compare arrays and suffix deprecated fields.
            //       DO NOT suffix fields that are already suffixed.

            foreach ( $Table->Fields as $strField ) {

                // TODO: Compare fields and suffix changed fields with '-old'.
                // Create new fields.

            }

        }

    }



// TODO: REWRITE THE OLD CLASS GENERATOR


   /**
    * Generates classes representing tables and records.
    *
    * Currently only index 0 is used.
    *
    * @param array $arrXMLFilenames
    */
   public static function generateClasses ( $arrXMLFilenames ) {

      $objInterpreter = new Core_Data_Generator_XMLInterpreter( $arrXMLFilenames );
      
      foreach ( $objInterpreter->getTables() as $objTable ) {
         
         self::generateTableClass( $objTable );
         
         if ( $objTable->holdsRecord() ) {
       
            self::generateRecordClass( $objTable );

         }
         
      }
      
   }
   
   private static function generateTableClass ( $objTable ) {

      // Set the location of the templates.
      $strTemplatesLocation = "../application/library/core/data/generator/templates/";      


      // Setup the wrapper.
      $strClassWrapper = self::getTemplateContent( $strTemplatesLocation . "table.wrapper" );
      self::replace( "{PHPDocPackageName}", $objTable->getPackageName(), $strClassWrapper, $objTable->getPrefix() );
      self::replace( "{PHPDocSubpackageName}", $objTable->getSubpackageName(), $strClassWrapper, "Tables" );


      // Setup the methods.

      $strClassMethods = "";
      $strClassMethods .= self::getTemplateContent( $strTemplatesLocation . "table.method.count" ) . "\r\n\r\n";

      // Add methods that only applies if there's a separate record class.
      if ( $objTable->holdsRecord() ) {

         $strClassMethods .= self::getTemplateContent( $strTemplatesLocation . "table.method.getRecordByKey" ) . "\r\n\r\n";

         $strClassMethods .= self::getTemplateContent( $strTemplatesLocation . "table.method.add" ) . "\r\n\r\n";

         $strQueryFields = "";
         foreach ( $objTable->getFields() as $objField ) {

            if ( !$objField->isKey() ) {

              $strTemplateFilename = ( $objField->isRequired() ) ? "table.method.add.requiredfield" : "table.method.add.field";
              $strQueryField = self::getTemplateContent( $strTemplatesLocation . $strTemplateFilename ) . "\r\n\r\n";
              self::replace( "{FieldName}", $objField->getName(), $strQueryField );
              $strQueryFields .= $strQueryField;

            }

         }

         self::replace( "{QueryFields}", $strQueryFields, $strClassMethods );

      }       


      // Join the wrapper and the methods.
      $strClassContent = self::replace( "{ClassMethods}", $strClassMethods, $strClassWrapper, null, false );


      // Replace stuff that's common through all templates.
      self::replace( "{TablePrefix}", $objTable->getPrefix(), $strClassContent );
      self::replace( "{TableName}", $objTable->getName(), $strClassContent );
      self::replace( "{ClassName}", $objTable->getClassName(), $strClassContent, $objTable->getPrefix() . "_Data_Tables_" . $objTable->getName() );
         
      if ( $objTable->holdsRecord() ) {
         self::replace( "{RecordClassName}", $objTable->getRecord()->getClassName(), $strClassContent, $objTable->getPrefix() . "_Data_Records_" . $objTable->getRecord()->getName() );
      }
         

      // Save the class.
      $strClassFilename = self::getClassFilename( $objTable );
      self::saveClass( $strClassFilename, $strClassContent );
      
   }
   
   private static function generateRecordClass ( $objTable ) {

      // Set the location of the templates.
      $strTemplatesLocation = "../application/library/core/data/generator/templates/";


      // Setup the wrapper.
      $strClassWrapper = self::getTemplateContent( $strTemplatesLocation . "record.wrapper" );
      self::replace( "{PHPDocPackageName}", $objTable->getRecord()->getPackageName(), $strClassWrapper, $objTable->getPrefix() );
      self::replace( "{PHPDocSubpackageName}", $objTable->getRecord()->getSubpackageName(), $strClassWrapper, "Records" );


      // Setup the methods.

      $strClassMethods = "";
            
      foreach ( $objTable->getFields() as $objField ) {

         // Setup the get function.

         $arrTemplateFilename = array(
               "boolean" => "record.method.get.boolean",
               "email" => "record.method.get",
               "int8" => "record.method.get",
               "int16" => "record.method.get",
               "int24" => "record.method.get",
               "int32" => "record.method.get",
               "int64" => "record.method.get",
               "md5" => "record.method.get",
               "text" => "record.method.get",
               "unixtime32" => "record.method.get",
               "unixtime64" => "record.method.get"
         );

         $strMethodContent = self::getTemplateContent( $strTemplatesLocation . $arrTemplateFilename[$objField->getDatatype()] );

         $strMethodPrefix = ( $objField->getDatatype() == "boolean" ) ? "is" : "get";
         self::replace( "{FunctionName}", $strMethodPrefix . $objField->getName(), $strMethodContent );
         self::replace( "{FieldName}", $objField->getName(), $strMethodContent );
         
         if ( $objField->getDatatype() == "unixtime32" or $objField->getDatatype() == "unixtime64" ) {
            self::replace( "{PHPDocReturn}", $objField->getDatatype( Core_Data_Generator_XMLInterpreter::PHP ) . " Unix time.", $strMethodContent );
         } else {
            self::replace( "{PHPDocReturn}", $objField->getDatatype( Core_Data_Generator_XMLInterpreter::PHP ), $strMethodContent );
         }
            
         if ( $objField->isKey() ) {
            self::replace( "{PHPDocShortDescription}", "Gets the key.", $strMethodContent );
         }
         
         self::replace( "{PHPDocShortDescription}", "Gets the value of the field '" . $objField->getName() . "'.", $strMethodContent );
             
               
         $strClassMethods .= $strMethodContent . "\r\n\r\n";
               
         
         // Setup the update function.
               
         if ( !$objField->isKey() ) {

            $arrTemplateFilename = array(
                  "boolean" => "record.method.update.boolean",
                  "email" => "record.method.update.email",
                  "int8" => "record.method.update",
                  "int16" => "record.method.update",
                  "int24" => "record.method.update",
                  "int32" => "record.method.update",
                  "int64" => "record.method.update",
                  "md5" => "record.method.update.md5",
                  "text" => "record.method.update",
                  "unixtime32" => "record.method.update.unixtime",
                  "unixtime64" => "record.method.update.unixtime"
            );

            $strMethodContent = self::getTemplateContent( $strTemplatesLocation . $arrTemplateFilename[$objField->getDatatype()] );

            self::replace( "{FunctionName}", "set" . $objField->getName(), $strMethodContent );
            self::replace( "{FieldName}", $objField->getName(), $strMethodContent );
            self::replace( "{PHPDocShortDescription}", "Sets the value of the field '" . $objField->getName() . "'.", $strMethodContent );
                  
            if ( $objField->getDatatype() == "md5" ) {
               self::replace( "{PHPDocLongDescription}", "An MD5 hash of <var>\$strValue</var> will be stored.", $strMethodContent );   
            }
                  
            $strClassMethods .= $strMethodContent . "\r\n\r\n";
                  
         }
               
      }
      
      
      // Join the wrapper and the methods.
      $strClassContent = self::replace( "{ClassContent}", $strClassMethods, $strClassWrapper, null, false ); 
         
      
      // Replace stuff that's common through all templates.
      self::replace( "{TablePrefix}", $objTable->getPrefix(), $strClassContent );
      self::replace( "{TableName}", $objTable->getName(), $strClassContent );
      self::replace( "{ClassName}", $objTable->getRecord()->getClassName(), $strClassContent, $objTable->getPrefix() . "_Data_Records_" . $objTable->getRecord()->getName() );
                               
                      
      // Save the class.
      $strClassFilename = self::getClassFilename( $objTable->getRecord() );
      self::saveClass( $strClassFilename, $strClassContent );
                  
   }

   /**
    * Gets the content of a template file.
    *
    * @param string $strTemplateFilename
    * @return string
    */
   private static function getTemplateContent ( $strTemplateFilename ) {

      $resFile = fopen( $strTemplateFilename, "r" );
      $strTemplateContent = fread( $resFile, filesize( $strTemplateFilename ) );
      fclose( $resFile );
      
      return $strTemplateContent;
         
   }
   
   private static function getClassFilename ( $objInterpreter ) {

      if ( $objInterpreter->getClassFilename() ) {
         $strClassFilename = $objInterpreter->getClassFilename();
      } elseif ( $objInterpreter->getClassname() ) {
         $strClassFilename = "../application/library/" . str_replace( "_", "/", $objInterpreter->getClassname() ) . ".php";
      } else {

         if ( $objInterpreter instanceof Core_Data_Generator_XMLInterpreter_Table ) {
            $strClassFilename = "../application/library/" . $objInterpreter->getPrefix() . "/data/tables/" . $objInterpreter->getName() . ".php";
         } elseif ( $objInterpreter instanceof Core_Data_Generator_XMLInterpreter_Record ) {
            $strClassFilename = "../application/library/" . $objInterpreter->getTable()->getPrefix() . "/data/records/" . $objInterpreter->getName() . ".php";
         }

      }

      return $strClassFilename;

   }
   
   private static function saveClass ( $strClassFilename, $strClassContent ) {
      
      $resFile = fopen( $strClassFilename, "w" );
      fwrite( $resFile, $strClassContent );
      fclose( $resFile );
            
   }
   
   private static function replace ( $strSearch, $strReplace, &$strSubject, $strDefault = "", $boolSubjectByReference = true ) {
    
      $strReplace = ( $strReplace ) ? $strReplace : $strDefault; 
      
      $strResult = str_replace( $strSearch, $strReplace, $strSubject );
      
      if ( $boolSubjectByReference ) {
         $strSubject = $strResult;
      } else {
         return $strResult;
      }
      
   }

}

?>
